import java.util.LinkedList;
import java.util.Observable;
import java.util.Observer;

/*
 * ****************************
 * Eric Doherty
 * Jesse Kinney
 * 
 * TCSS 422 Scheduler simulator
 * Scheduler Class
 * 
 * 6/4/08
 * ****************************
 */

public class Scheduler implements Observer
{
  /*
   * Scheduler - Called when the CPU receives an interrupt and when a
   * process makes a system call. - It determines what happened to activate
   * it - Do context switching to enforce round robin policy - Manages ready
   * and blocked queues, I/O queues, Semaphore Queues.
   */

  private Process running;

  private LinkedList<Process> blockedIO1;

  private LinkedList<Process> blockedIO2;

  private LinkedList<Process> blockedFull;

  private LinkedList<Process> blockedEmpty;

  private LinkedList<Process> blockedCrit;

  private LinkedList<Process> readyToRun;

  public Scheduler(LinkedList<Process> rtr)
  {
    blockedIO1 = new LinkedList<Process>();
    blockedIO2 = new LinkedList<Process>();
    blockedFull = new LinkedList<Process>();
    blockedEmpty = new LinkedList<Process>();
    blockedCrit = new LinkedList<Process>();
    readyToRun = rtr;
    for (Process p : readyToRun)
      p.addObserver(this);
  }

  public void schedule()
  {
    if (readyToRun.size() == 0)
    {
      System.out.println("All processes blocking");
      running = null;
    }
    else
    {
      running = readyToRun.poll();
      System.out.println("Scheduling... Running contains process "
          + running.getPid());

      if (running != null)
      {
        if (running.getClass().getSimpleName().equals("CalculatingProcess"))
        {
          System.out.println("Calculating...");
        }
      }
    }
  }

  public Process getRunning()
  {
    return running;
  }

  public void setRunning(Process process)
  {
    running = process;
  }

  public void switchOutRunning()
  {
    if (running != null)
    {
      readyToRun.offer(running);
    }
  }

  // Returns true if a process was unblocked from the given queue
  public boolean unblock(LinkedList<Process> blockedQueue)
  {
    if (!blockedQueue.isEmpty())
    {
      Process unblocked = blockedQueue.poll();
      System.out.println("Unblocking Process " + unblocked.getPid());
      readyToRun.offer(unblocked);
      return true;
    }
    else
    {
      return false;
    }
  }

  public void update(Observable o, Object obj)
  {
    if (obj != null)
    {
      Event e = (Event) obj;

      // Unblocking
      if (e.getType() == Event.EventTypes.UNBLOCKC)
      {
        if (!unblock(blockedFull))
        {
          unblock(blockedCrit);
        }
      }
      else if (e.getType() == Event.EventTypes.UNBLOCKP)
      {
        if (!unblock(blockedEmpty))
        {
          unblock(blockedCrit);
        }
      }
      else if (e.getType() == Event.EventTypes.UNBLOCKIO1)
      {
        unblock(blockedIO1);
      }
      else if (e.getType() == Event.EventTypes.UNBLOCKIO2)
      {
        unblock(blockedIO2);
      }
      // blocking
      else
      {
        if (e.getType() == Event.EventTypes.STARTIO1)
        {
          blockedIO1.add(running);
        }
        else if (e.getType() == Event.EventTypes.STARTIO2)
        {
          blockedIO2.add(running);
        }
        else if (e.getType() == Event.EventTypes.EMPTY)
        {
          blockedEmpty.add(running);
        }
        else if (e.getType() == Event.EventTypes.FULL)
        {
          blockedFull.add(running);
        }
        else if (e.getType() == Event.EventTypes.CRIT)
        {
          blockedCrit.add(running);
        }
        schedule();
      }
    }
  }
}
